// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

/*============================================================
**
** Source: CreateSemaphoreW_ReleaseSemaphore/test2/CreateSemaphore.c
**
** Purpose: Test Semaphore operation using classic IPC problem:
** "Producer-Consumer Problem".
**
** Dependencies: CreateThread
**               ReleaseSemaphore
**               WaitForSingleObject
**               Sleep
**               fflush
**

**
**=========================================================*/

#define UNICODE
#include <palsuite.h>

#define PRODUCTION_TOTAL 26

#define _BUF_SIZE 10

DWORD dwThreadId_CreateSemaphoreW_test2;  /* consumer thread identifier */

HANDLE hThread_CreateSemaphoreW_test2; /* handle to consumer thread */

HANDLE hSemaphoreM_CreateSemaphoreW_test2; /* handle to mutual exclusion semaphore */

HANDLE hSemaphoreE_CreateSemaphoreW_test2; /* handle to semaphore that counts empty buffer slots */

HANDLE hSemaphoreF_CreateSemaphoreW_test2; /* handle to semaphore that counts full buffer slots */

typedef struct Buffer
{
    short readIndex;
    short writeIndex;
    CHAR message[_BUF_SIZE];

} BufferStructure;

CHAR producerItems_CreateSemaphoreW_test2[PRODUCTION_TOTAL + 1];

CHAR consumerItems_CreateSemaphoreW_test2[PRODUCTION_TOTAL + 1];

/*
 * Read next message from the Buffer into provided pointer.
 * Returns:  0 on failure, 1 on success.
 */
int
readBuf_CreateSemaphoreW_test2(BufferStructure *Buffer, char *c)
{
    if( Buffer -> writeIndex == Buffer -> readIndex )
    {
	return 0;
    }
    *c = Buffer -> message[Buffer -> readIndex++];
    Buffer -> readIndex %= _BUF_SIZE;
    return 1;
}

/*
 * Write message generated by the producer to Buffer.
 * Returns:  0 on failure, 1 on success.
 */
int
writeBuf_CreateSemaphoreW_test2(BufferStructure *Buffer, CHAR c)
{
    if( ( ((Buffer -> writeIndex) + 1) % _BUF_SIZE) ==
	(Buffer -> readIndex) )
    {
	return 0;
    }
    Buffer -> message[Buffer -> writeIndex++] = c;
    Buffer -> writeIndex %= _BUF_SIZE;
    return 1;
}

/*
 * Atomic decrement of semaphore value.
 */
VOID
down_CreateSemaphoreW_test2(HANDLE hSemaphore)
{
    switch ( (WaitForSingleObject (
		  hSemaphore,
		  10000)))      /* Wait 10 seconds */
    {
    case WAIT_OBJECT_0:  /*
			  * Semaphore was signaled. OK to access
			  * semaphore.
			  */
	break;
    case WAIT_ABANDONED: /*
			  * Object was mutex object whose owning
			  * thread has terminated.  Shouldn't occur.
			  */
	Fail("WaitForSingleObject call returned 'WAIT_ABANDONED'.\n"
	     "Failing Test.\n");
	break;
    case WAIT_FAILED:    /* WaitForSingleObject function failed */
	Fail("WaitForSingleObject call returned 'WAIT_FAILED'.\n"
	     "GetLastError returned %d\nFailing Test.\n",GetLastError());
	break;
    default:
	Fail("WaitForSingleObject call returned an unexpected value.\n"
	     "Failing Test.\n");
	break;
    }

}

/*
 * Atomic increment of semaphore value.
 */
VOID
up_CreateSemaphoreW_test2(HANDLE hSemaphore)
{
    if (!ReleaseSemaphore (
	    hSemaphore,
	    1,
	    NULL)
	)
    {
	Fail("ReleaseSemaphore call failed.  GetLastError returned %d\n",
	     GetLastError());
    }
}

/*
 * Sleep 500 milleseconds.
 */
VOID
consumerSleep_CreateSemaphoreW_test2(VOID)
{
    Sleep(10);
}

/*
 * Sleep between 10 milleseconds.
 */
VOID
producerSleep_CreateSemaphoreW_test2(VOID)
{
    Sleep(500);
}

/*
 * Produce a message and write the message to Buffer.
 */
VOID
producer_CreateSemaphoreW_test2(BufferStructure *Buffer)
{

    int n = 0;
    char c;

    while (n < PRODUCTION_TOTAL)
    {
	c = 'A' + n ;   /* Produce Item */

	down_CreateSemaphoreW_test2(hSemaphoreE_CreateSemaphoreW_test2);
	down_CreateSemaphoreW_test2(hSemaphoreM_CreateSemaphoreW_test2);

	if (writeBuf_CreateSemaphoreW_test2(Buffer, c))
	{
            Trace("Producer produces %c.\n", c);
	    fflush(stdout);
	    producerItems_CreateSemaphoreW_test2[n++] = c;
	}

	up_CreateSemaphoreW_test2(hSemaphoreM_CreateSemaphoreW_test2);
	up_CreateSemaphoreW_test2(hSemaphoreF_CreateSemaphoreW_test2);

	producerSleep_CreateSemaphoreW_test2();
    }

    return;
}

/*
 * Read and "Consume" the messages in Buffer.
 */
DWORD
PALAPI
consumer_CreateSemaphoreW_test2( LPVOID lpParam )
{
    int n = 0;
    char c;

    consumerSleep_CreateSemaphoreW_test2();

    while (n < PRODUCTION_TOTAL)
    {

	down_CreateSemaphoreW_test2(hSemaphoreF_CreateSemaphoreW_test2);
	down_CreateSemaphoreW_test2(hSemaphoreM_CreateSemaphoreW_test2);

	if (readBuf_CreateSemaphoreW_test2((BufferStructure*)lpParam, &c))
	{
	    Trace("\tConsumer consumes %c.\n", c);
	    fflush(stdout);
	    consumerItems_CreateSemaphoreW_test2[n++] = c;
	}

	up_CreateSemaphoreW_test2(hSemaphoreM_CreateSemaphoreW_test2);
	up_CreateSemaphoreW_test2(hSemaphoreE_CreateSemaphoreW_test2);

	consumerSleep_CreateSemaphoreW_test2();
    }

    return 0;
}

PALTEST(threading_CreateSemaphoreW_ReleaseSemaphore_test2_paltest_createsemaphorew_releasesemaphore_test2, "threading/CreateSemaphoreW_ReleaseSemaphore/test2/paltest_createsemaphorew_releasesemaphore_test2")
{
    BufferStructure Buffer, *pBuffer;

    pBuffer = &Buffer;

    if(0 != (PAL_Initialize(argc, argv)))
    {
	return (FAIL);
    }

    /*
     * Create Semaphores
     */
    hSemaphoreM_CreateSemaphoreW_test2 = CreateSemaphoreExW (
	NULL,
	1,
	1,
	NULL,
	0,
	0);

    if ( NULL == hSemaphoreM_CreateSemaphoreW_test2 )
    {
	Fail ( "hSemaphoreM_CreateSemaphoreW_test2 = CreateSemaphoreExW () - returned NULL\n"
	       "Failing Test.\n");
    }

    hSemaphoreE_CreateSemaphoreW_test2 = CreateSemaphoreExW (
	NULL,
	_BUF_SIZE ,
	_BUF_SIZE ,
	NULL,
	0,
	0);

    if ( NULL == hSemaphoreE_CreateSemaphoreW_test2 )
    {
	Fail ( "hSemaphoreE_CreateSemaphoreW_test2 = CreateSemaphoreExW () - returned NULL\n"
	       "Failing Test.\n");
    }

    hSemaphoreF_CreateSemaphoreW_test2 = CreateSemaphoreExW (
	NULL,
	0,
	_BUF_SIZE ,
	NULL,
	0,
	0);

    if ( NULL == hSemaphoreF_CreateSemaphoreW_test2 )
    {
	Fail ( "hSemaphoreF_CreateSemaphoreW_test2 = CreateSemaphoreExW () - returned NULL\n"
	       "Failing Test.\n");
    }

    /*
     * Initialize Buffer
     */
    pBuffer->writeIndex = pBuffer->readIndex = 0;

    /*
     * Create Consumer
     */
    hThread_CreateSemaphoreW_test2 = CreateThread(
	NULL,
	0,
	consumer_CreateSemaphoreW_test2,
	&Buffer,
	0,
	&dwThreadId_CreateSemaphoreW_test2);

    if ( NULL == hThread_CreateSemaphoreW_test2 )
    {
	Fail ( "CreateThread() returned NULL.  Failing test.\n");
    }

    /*
     * Start producing
     */
    producer_CreateSemaphoreW_test2(pBuffer);

    /*
     * Wait for consumer to complete
     */
    WaitForSingleObject (hThread_CreateSemaphoreW_test2, INFINITE);

    if ( 0 != strncmp (producerItems_CreateSemaphoreW_test2, consumerItems_CreateSemaphoreW_test2, PRODUCTION_TOTAL) )
    {
	Fail("The producerItems_CreateSemaphoreW_test2 string %s\n and the consumerItems_CreateSemaphoreW_test2 string "
	     "%s\ndo not match. This could be a problem with the strncmp()"
	     " function\n FailingTest\nGetLastError() returned %d\n",
	     producerItems_CreateSemaphoreW_test2, consumerItems_CreateSemaphoreW_test2, GetLastError());
    }

    Trace ("producerItems_CreateSemaphoreW_test2 and consumerItems_CreateSemaphoreW_test2 arrays match.  All %d\nitems "
	   "were produced and consumed in order.\nTest passed.\n",
	   PRODUCTION_TOTAL);

    PAL_Terminate();
    return ( PASS );

}
